除了後開先關的規則之外,在使用 try-with-resource 語法時,還有一點要注意的,就是 例外的壓制(Exception Suppressed)。
我們已經知道 close 方法有可能會丟出例外,那如果在 try 區塊裡也不小心丟出例外時,Java 會怎麼處理這兩個例外呢?我們先宣設計另一個可關閉的類別:
package idv.jacky.ironman4.day24;
public class MyResource3 implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("Close resource 3.");
throw new Exception("MyResource3 Close Exception");
}
}
在 MyResource3.close 方法裡,我們它丟出一個 Exception 例外。然後我們這樣來測試一下:
package idv.jacky.ironman4.day24;
public class Day24Example1 {
public static void main(String[] args) {
try {
foo();
} catch (Exception e) {
System.out.println(e);
}
}
public static void foo() throws Exception {
try (MyResource3 r3 = new MyResource3()) {
System.out.println("Do something...");
throw new Exception("Something error!");
}
}
}
我們有個 foo 的方法,方法裡會產生 MyResource3 的物件,然後在 foo 方法的 try 區塊裡也丟出一個例外。而呼叫 foo 的 main 方法裡,用個try-catch 區塊來補捉 foo 方法所丟出來的例外。最後你猜猜在程式的第9行會印出什麼?
很意外嗎? 在 close 方法裡的 "MyResource3 Close Exception" 跑哪去?因為 catch 陳述式裡一次只能補捉一個例外,try 區塊中已經丟出一個例外了,Java 會優先補捉它。一般來說 try 區塊的程式碼是主要的,所以有例外發生時,通常會需要特地去處理;而 close 所丟出來的例外是次要的,你可以依情況進一步處理。
close 方法丟出的例外並沒有不見,只是被壓制(Suppressed)了,Java SE 7 的 Throwable 介面裡(所有的 Exception 都實作這個介面)有個 getSuppressed 的方法,它就是用來取出所有被壓制的例外!我們來改寫一下剛剛的例子:
package idv.jacky.ironman4.day24;
public class Day24Example2 {
public static void main(String[] args) {
try {
foo();
} catch (Exception e) {
System.out.println(e);
System.out.println("Suppressed Exceptions: ");
Throwable[] th = e.getSuppressed();
for(Throwable t : th)
System.out.println(t);
}
}
public static void foo() throws Exception {
try (MyResource3 r3 = new MyResource3()) {
System.out.println("Do something...");
throw new Exception("Something error!");
}
}
}
我們在 main 方法裡補捉到例外後(第21行丟出來的),呼叫 getSuppressed 方法來取得被壓制的例外,也就是 MyResource3 裡第8行所丟出來的。getSuppressed 會回傳一個 Throwable 物件陣列,因為 try-with-resource 陳述式裡可以有多個可關閉物件的宣告。然後我們再用個 for 迴圈把這裡被壓制的例外也印出來(本範例中只有一個),程式執行結果如下:
現在你知道 MyResource3.close 方法丟出的例外跑哪去了吧!